package net.pingfang.core;

import lombok.extern.slf4j.Slf4j;
import net.pingfang.config.RestTemplateConfig;
import net.pingfang.constant.GpsConstant;
import net.pingfang.model.common.FixSizeLinkedList;
import net.pingfang.model.common.Result;
import net.pingfang.model.dto.GpsDto;
import net.pingfang.model.dto.QueryGpsResDTO;
import net.pingfang.util.ExceptionUtil;
import net.pingfang.util.HexUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Author zengqifeng
 * @CreateTime 2023/7/25 11:47
 */
@Slf4j
@Component
public class GpsReceiveService implements CommandLineRunner {


    //public static final ConcurrentHashMap<String, Socket> safeOperateSocketMap = new ConcurrentHashMap<>();
    public static final String PF_SERVER_ = "PF_SERVER_";
    public static final Map<String, QueryGpsResDTO> gpsMap = new HashMap<>();
    public static final ConcurrentHashMap<String, FixSizeLinkedList<GpsDto>> locationMap = new ConcurrentHashMap<>();

    @Value("${safe.gps.socket.port:12347}")
    private Integer safeGpsSocketPort;

    @Value("${gps.send.open}")
    private Boolean sendOpen;

    @Value("${gps.send.url}")
    private String gpsUrl;


    ExecutorService pool = Executors.newFixedThreadPool(5);

    @Override
    public void run(String... args) {
        new Thread(() -> {
            try {
                //服务端启动，监听端口12345，等待客户端连接
                ServerSocket serverSocket = new ServerSocket(safeGpsSocketPort);
                log.info("开始接收GPS数据服务启动成功,端口:{},开始监听连接", safeGpsSocketPort);
                while (true) {
                    //等待客户端连接，阻塞式方法
                    Socket socket = serverSocket.accept();
                    //判断连接的平台是否是socket
                    String hostAddress = socket.getInetAddress().getHostAddress();
                    log.info("客户端:{}已连接！", hostAddress);
                    //gps终端的连接
                    //启动读取客户端消息的子线程
                    pool.execute(new ClientHandler(socket,sendOpen,gpsUrl));
                }
            } catch (Exception exception) {
                log.error("启动监听服务异常：{}", ExceptionUtil.getStackTrace(exception));
            }
        }).start();
    }

    public Result<FixSizeLinkedList<GpsDto>> queryGps(String gpsUniqueCode){
        String gpsData = "";
        if(CollectionUtils.isEmpty(gpsMap)){
            return Result.success(gpsData);
        }
        new QueryGpsResDTO();
        QueryGpsResDTO gpsResDTO;
        for (Map.Entry<String,QueryGpsResDTO> key : gpsMap.entrySet()){
            if(key.getKey().equals(gpsUniqueCode)){
                gpsResDTO = key.getValue();
                gpsData = gpsResDTO.getLongitude()+","+gpsResDTO.getLatitude();
            }
        }
        return Result.success(locationMap.get(gpsUniqueCode),gpsData);
    }

    static class ClientHandler implements Runnable{
        private final Socket socket;
        private final Boolean sendOpen;
        private final String gpsUrl;


        public ClientHandler(Socket socket,Boolean sendOpen,String gpsUrl) {
            log.info("创建ClientHandler！");
            this.socket = socket;
            this.sendOpen = sendOpen;
            this.gpsUrl = gpsUrl;
        }


        @Override
        public void run() {
            try  {
                //socket 输入流 用于接收客户端消息
                InputStream in = socket.getInputStream();
                //socket 输出流 服务端箱客户端回复消息
                OutputStream os = socket.getOutputStream();
                //接收数据的byte数组
                byte[] bys = new byte[1024];
                //存储长度
                int len;
                while ((len = in.read(bys)) != -1) {
                    log.info("读取数据中！");
                    /*String message = new String(bys, 0, len, StandardCharsets.UTF_8);
                    if (!StringUtils.isEmpty(message) && message.startsWith(PF_SERVER_)){
                        String substring = message.trim().substring(10, 22);
                        log.info("应用程序连接:{}", substring);
                        synchronized (safeOperateSocketMap) {
                            safeOperateSocketMap.put(substring, socket);
                        }
                        continue;
                    }*/
                    StringBuilder bytStr = new StringBuilder();
                    for (Byte b : bys){
                        bytStr.append(b);
                    }
                    log.info(bytStr.toString());

                    //转换成16进制字符串
                    String messageHex = HexUtil.bytesToHexString(bys);
                    //进行转义 7d02 --> 7e    7d01 -->7d
                    if (messageHex != null) {
                        messageHex = messageHex.replaceAll(GpsConstant.d702, GpsConstant.e7);
                    }
                    if (messageHex != null) {
                        messageHex = messageHex.replaceAll(GpsConstant.d701, GpsConstant.d7);
                    }
                    //长度不足20的直接丢弃
                    if (messageHex !=null && messageHex.length() <= 20) {
                        continue;
                    }
                    log.info("收到GPS数据16进制转字符串：{}", messageHex);

                    //解析消息
                    //消息id 4位
                    String id = null;
                    if (messageHex != null) {
                        id = messageHex.substring(2, 6);
                    }
                    //消息体长度 4位
                    String msgLength = null;
                    if (messageHex != null) {
                        msgLength = messageHex.substring(6, 10);
                    }
                    //设备唯一号 12位
                    String gpsUniqueCode = null;
                    if (messageHex != null) {
                        gpsUniqueCode = messageHex.substring(10, 22);
                    }
                    //消息流水号
                    String seq = null;
                    if (messageHex != null) {
                        seq = messageHex.substring(22, 26);
                    }
                    log.info("消息id：{}， 长度：{}， 设备唯一号：{}， 消息流水号：{}", id, msgLength, gpsUniqueCode, seq);
                    //响应
                    String resId = null;
                    String resStr = null;
                    if (GpsConstant.registerSendId.equals(id)){
                        //注册
                        resId = GpsConstant.registerReceiveId;
                        resStr = buildRegisterResStr(resId, GpsConstant.registerMessageLength, gpsUniqueCode, seq, GpsConstant.successCode, GpsConstant.registerValidateCode);
                    }
                    if (GpsConstant.validateSendId.equals(id)){
                        //鉴权
                        resId = GpsConstant.validateReceiveId;
                        resStr = buildValidateResStr(resId, GpsConstant.validateMessageLength, gpsUniqueCode, seq, GpsConstant.validateSendId, GpsConstant.successCode);
                    }
                    if (GpsConstant.positionSendId.equals(id)){
                        //位置信息
                        resId = GpsConstant.positionReceiveId;
                        //解析出经度、维度
                        String latitude = String.valueOf(Integer.parseInt(messageHex.substring(42, 50), 16));
                        String longitude =String.valueOf(Integer.parseInt( messageHex.substring(50, 58), 16));
                        String altitude = String.valueOf(Integer.parseInt(messageHex.substring(58, 62), 16));
                        String speed = String.valueOf(Integer.parseInt(messageHex.substring(62, 66), 16));
                        String direction = String.valueOf(Integer.parseInt(messageHex.substring(66, 70), 16));
                        String dateTime = messageHex.substring(70, 82);
                        //信号强度
                        Integer signalStrength = Integer.parseInt(messageHex.substring(98, 100), 16);
                        //电量
                        Integer quantityOfElectricity = Integer.parseInt(messageHex.substring(118, 122), 16);
                        //定位状态
                        Integer positioningStatus = Integer.parseInt(messageHex.substring(128, 130));
                        //经度扩展
                        Integer extend2longitude = Integer.parseInt(messageHex.substring(130, 132));
                        //纬度扩展
                        Integer extend2latitude = Integer.parseInt(messageHex.substring(132, 134));
                        //定位状态
                        log.info("纬度：{}，经度：{}，海拔高度：{}，速度：{}，方向：{}，日期时间：{}, 信号强度：{}， 电量：{}，定位状态：{}， 经度扩展：{}， 纬度扩展：{}", latitude, longitude, altitude, speed, direction, dateTime, signalStrength, quantityOfElectricity, positioningStatus, extend2longitude, extend2latitude);
                        if (GpsConstant.zero.equals(latitude) || GpsConstant.zero.equals(longitude)) {
                            log.info("经纬度数据为0，直接丢弃");
                            continue;
                        }


                        //存入map
                        QueryGpsResDTO gpsResDTO = new QueryGpsResDTO();
                        gpsResDTO.setLatitude(latitude);
                        gpsResDTO.setLongitude(longitude);
                        gpsMap.put(gpsUniqueCode,gpsResDTO);


                        GpsDto gpsDto = new GpsDto();
                        gpsDto.setLatitude(String.valueOf(new BigDecimal(latitude).divide(BigDecimal.valueOf(1000000))));
                        gpsDto.setLongitude(String.valueOf(new BigDecimal(longitude).divide(BigDecimal.valueOf(1000000))));
                        gpsDto.setAltitude(altitude)
                                .setDirection(direction)
                                .setDateTime(dateTime)
                                .setExtendLatitude(extend2latitude)
                                .setSeq(seq)
                                .setInfoId(id)
                                .setMsgLength(msgLength)
                                .setSpeed(speed)
                                .setExtendLongitude(extend2longitude)
                                .setGpsUniqueCode(gpsUniqueCode)
                                .setQuantityOfElectricity(quantityOfElectricity)
                                .setPositioningStatus(positioningStatus)
                                .setSignalStrength(signalStrength);
                        if (CollectionUtils.isEmpty(locationMap.get(gpsUniqueCode))){
                            log.info("创建"+gpsUniqueCode+"的定位数据集合");
                            FixSizeLinkedList<GpsDto> locationList = new FixSizeLinkedList<>(5);
                            locationMap.put(gpsUniqueCode,locationList);
                        }
                        locationMap.get(gpsUniqueCode).add(gpsDto);

                        //发送gps数据给接收服务接口
                        if (sendOpen){
                            log.info("发送gps数据给："+gpsUrl);
                            try{
                                RestTemplateConfig.getInstance().postForObject(gpsUrl, gpsDto, Result.class);
                                log.info("发送完成:"+gpsDto);
                            }catch (Exception e){
                                log.info("发送失败:"+gpsDto);
                                log.info(e.getMessage());
                            }
                        }
//                        Socket safeOperateSocket = GpsReceiveService.safeOperateSocketMap.get(gpsUniqueCode);
//                        if (safeOperateSocket == null) {
//                            log.info("平台服务客户端未连接过来，将不发送位置信息！");
//                        }else {
//                            try (OutputStream safeOperateOs = safeOperateSocket.getOutputStream();){
////                            log.info("向平台发送位置信息：{}", JsonUtil.toJson(reqDTO));
//                                safeOperateOs.write((longitude+","+latitude).getBytes(StandardCharsets.UTF_8));
//
//                            }catch (Exception e) {
//                                log.info("向平台发送位置信息异常：{}", ExceptionUtil.getStackTrace(e));
//                                //断开连接并从map中丢弃
//                                safeOperateSocket.close();
//                                GpsReceiveService.safeOperateSocketMap.remove(gpsUniqueCode);
//                            }
//                        }
                        resStr = buildPositionResStr(resId, gpsUniqueCode, seq);
                    }
                    if (GpsConstant.heartbeatSendId.equals(id)){
                        //心跳
                        resId = GpsConstant.heartbeatReceiveId;
                        resStr = buildHeartbeatResStr(resId, GpsConstant.heartbeatMessageLength, gpsUniqueCode, seq, GpsConstant.heartbeatSendId, GpsConstant.successCode);
                    }
                    if (StringUtils.isEmpty(resId)) {
                        log.info("未匹配到resId，丢弃");
                        socket.close();
                        continue;
                    }
                    //消息转换成数组
                    String validateCode = HexUtil.calculateChecksum(resStr);
                    StringBuffer sb = new StringBuffer();
                    resStr = sb.append(GpsConstant.startEndFlag).append(resStr).append(validateCode).append(GpsConstant.startEndFlag).toString();
                    log.info("回复的消息:{}", resStr);
                    byte[] resBytes = HexUtil.hexTobytes(resStr);
                    os.write(resBytes);
                    os.flush();
                }
            }catch (Exception e) {
                log.info("客户端处理异常：{}", ExceptionUtil.getStackTrace(e));
                try {
                    socket.close();
                } catch (IOException ex) {
                    log.info("gps接收消息处理线程异常：{}", ExceptionUtil.getStackTrace(ex));
                }
            }
//            finally {
//                try {
//                    socket.close();
//                } catch (IOException ex) {
//                    log.info("gps接收消息处理线程异常：{}", ExceptionUtil.getStackTrace(ex));
//                }
//            }
        }

        private String buildHeartbeatResStr(String resId, String heartbeatMessageLength, String gpsUniqueCode, String seq, String heartbeatSendId, String successCode) {
            StringBuffer sb = new StringBuffer();
            return sb.append(resId).append(heartbeatMessageLength).append(gpsUniqueCode).append(seq).append(seq).append(heartbeatSendId).append(successCode).toString();
        }

        private String buildPositionResStr(String resId, String gpsUniqueCode, String seq) {
            StringBuffer sb = new StringBuffer();
            return sb.append(resId).append(GpsConstant.positionMessageLength).append(gpsUniqueCode).append(seq).append(seq).append(GpsConstant.positionSendId).append(GpsConstant.successCode).toString();
        }


        private String buildValidateResStr(String resId, String validateMessageLength, String gpsUniqueCode, String seq, String validateSendId, String successCode) {
            StringBuffer sb = new StringBuffer();
            return sb.append(resId).append(validateMessageLength).append(gpsUniqueCode).append(seq).append(seq).append(validateSendId).append(successCode).toString();
        }

        private String buildRegisterResStr(String resId, String registerMessageLength, String gpsUniqueCode, String seq, String successCode, String registerValidateCode) {
            StringBuffer sb = new StringBuffer();
            return sb.append(resId).append(registerMessageLength).append(gpsUniqueCode).append(seq).append(seq).append(successCode).append(registerValidateCode).toString();
        }

    }

}
